﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;

using Odelle.Utils;

namespace Odelle.WoWArmory.Data
{
    public class CharacterData
    {
        public static void IdToNames(string id, out string region, out string realm, out string character)
        {
            string[] names = id.Split('/');
            if (names.Length != 3)
            {
                region = "";
                realm = "";
                character = "";
            }
            else
            {
                region = names[0];
                realm = names[1];
                character = names[2];
            }
        }

        public static string NamesToId(string region, string realm, string character)
        {
            if (region == "" || realm == "" || character == "")
            {
                return "";
            }
            else
            {
                System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.GetCultureInfo("en-US");
                System.Globalization.TextInfo tInfo = culture.TextInfo;

                return tInfo.ToUpper(region) + "/" + realm + "/" + tInfo.ToTitleCase(character);
            }
        }

        [XmlIgnoreAttribute]
        public String ID
        {
            set
            {
                string region = "";
                string realm = "";
                string character = "";

                CharacterData.IdToNames(ID, out region, out realm, out character);

                this.Region = region;
                this.Realm = realm;
                this.Character = character;
            }
            get
            {
                return CharacterData.NamesToId(this.Region, this.Realm, this.Character);
            }
        }

        [XmlAttribute()]
        public String Region { set; get; }
        [XmlAttribute()]
        public string Realm { set; get; }
        [XmlAttribute()]
        public string Character { set; get; }

        [XmlAttribute()]
        public string Version { set; get; }
        [XmlAttribute()]
        public DateTime LastLogin { set; get; }
        [XmlAttribute("Updated")]
        public DateTime LastUpdated { set; get; }
        [XmlAttribute("Modified")]
        public string Modified
        {
            set
            {
                DateTime d;
                if (DateTime.TryParse(value, out d))
                    this.LastLogin = d;
            }
            get
            {
                return LastLogin.ToString();
            }

        }

        [XmlAttribute()]
        public string ErrCode { set; get; }

        [XmlAttribute()]
        public string Title { set; get; }
        [XmlAttribute()]
        public string ClassName { set; get; }

        public string Spec { get { return Spec1 + "/" + Spec2; } }
        [XmlAttribute()]
        public string Spec1 { set; get; }
        [XmlAttribute()]
        public string Spec2 { set; get; }
        [XmlAttribute()]
        public string Race { set; get; }
        [XmlAttribute()]
        public string Guild { set; get; }
        [XmlAttribute()]
        public int Level { set; get; }
        [XmlAttribute("Achieve")]
        public int Achievement { set; get; }

        [XmlAttribute("iLvlAve")]
        public double ItemLevel { set; get; }
        [XmlAttribute("iLvlEquip")]
        public double ItemLevelEquip { set; get; }

        // professions
        public string Profession { get { return Profession1 + "/" + Profession2; } }
        [XmlAttribute("Prof1")]
        public string Profession1 { set; get; }
        [XmlAttribute("Prof1val")]
        public int Profession1Value { set; get; }
        [XmlAttribute("Prof2")]
        public string Profession2 { set; get; }
        [XmlAttribute("Prof2val")]
        public int Profession2Value { set; get; }

        // main stats
        [XmlAttribute("Health")]
        public int BaseHealth { set; get; }
        [XmlAttribute("Mana")]
        public int BaseMana { set; get; }
        [XmlAttribute("Str")]
        public int BaseStrength { set; get; }
        [XmlAttribute("Sta")]
        public int BaseStamina { set; get; }
        [XmlAttribute("Agi")]
        public int BaseAgility { set; get; }
        [XmlAttribute("Int")]
        public int BaseIntellegence { set; get; }
        [XmlAttribute("Spi")]
        public int BaseSpirit { set; get; }
        [XmlAttribute("Mastery")]
        public double BaseMastery { set; get; }

        // stats
        [XmlAttribute("SplPower")]
        public double SpellPower { set; get; }
        [XmlAttribute("SplHaste")]
        public double SpellHaste { set; get; }
        [XmlAttribute("SplHit")]
        public double SpellHit { set; get; }
        [XmlAttribute("SplCrit")]
        public double SpellCrit { set; get; }
        [XmlAttribute("ManaRegen")]
        public double ManaRegenRest { set; get; }
        [XmlAttribute("CombatRegen")]
        public double ManaRegenCombat { set; get; }

        [XmlAttribute("MlePower")]
        public double MeleePower { set; get; }
        [XmlAttribute("MleDps")]
        public double MeleeDps { set; get; }
        [XmlAttribute("MleDps2")]
        public double MeleeDps2 { set; get; }
        [XmlAttribute("MleSpeed")]
        public double MeleeSpeed { set; get; }
        [XmlAttribute("MleSpeed2")]
        public double MeleeSpeed2 { set; get; }
        [XmlAttribute("MleHit")]
        public double MeleeHit { set; get; }
        [XmlAttribute("MleCrit")]
        public double MeleeCrit { set; get; }
        [XmlAttribute("MleHaste")]
        public double MeleeHaste { set; get; }
        [XmlAttribute("MleHaste2")]
        public double MeleeHaste2 { set; get; }
        [XmlAttribute()]
        public double Expertise { set; get; }
        [XmlAttribute()]
        public double Expertise2 { set; get; }

        [XmlAttribute("RngPower")]
        public double RangePower { set; get; }
        [XmlAttribute("RngDps")]
        public double RangeDps { set; get; }
        [XmlAttribute("RngSpeed")]
        public double RangeSpeed { set; get; }
        [XmlAttribute("RngHit")]
        public double RangeHit { set; get; }
        [XmlAttribute("RngCrit")]
        public double RangeCrit { set; get; }
        [XmlAttribute("RngHaste")]
        public double RangeHaste { set; get; }

        [XmlAttribute("Armor")]
        public double DefenceArmor { set; get; }
        [XmlAttribute("Dodge")]
        public double DefenceDodge { set; get; }
        [XmlAttribute("Parry")]
        public double DefenceParry { set; get; }
        [XmlAttribute("Block")]
        public double DefenceBlock { set; get; }
        [XmlAttribute("Resili")]
        public double Resilience { set; get; }
        [XmlAttribute("PvPPower")]
        public double PvpPower { set; get; }

        public CharacterData()
        {
            this.Version = "";
            this.Modified = "";
            this.LastUpdated = DateTime.Today;
            this.ErrCode = "";

            this.Region = "US";
            this.Realm = "";
            this.Character = "";
            this.Title = "";
            this.ClassName = "";
            this.Spec1 = "";
            this.Spec2 = "";
            this.Race = "";
            this.Guild = "";

            this.Level = 0;
            this.Achievement = 0;

            this.ItemLevelEquip = 0;
            this.ItemLevel = 0;

            this.BaseHealth = 0;
            this.BaseMana = 0;
            this.BaseStrength = 0;
            this.BaseStamina = 0;
            this.BaseAgility = 0;
            this.BaseIntellegence = 0;
            this.BaseSpirit = 0;
            this.BaseMastery = 0.0;

            this.SpellPower = 0;
            this.SpellHaste = 0;
            this.SpellHit = 0;
            this.SpellCrit = 0;
            this.ManaRegenRest = 0;
            this.ManaRegenCombat = 0;

            this.MeleePower = 0;
            this.MeleeDps = 0;
            this.MeleeDps2 = 0;
            this.MeleeSpeed = 0;
            this.MeleeSpeed2 = 0;
            this.MeleeHit = 0;
            this.MeleeCrit = 0;
            this.MeleeHaste = 0;
            this.MeleeHaste2 = 0;
            this.Expertise = 0;
            this.Expertise2 = 0;

            this.RangePower = 0;
            this.RangeDps = 0;
            this.RangeSpeed = 0;
            this.RangeHit = 0;
            this.RangeCrit = 0;
            this.RangeHaste = 0;

            this.DefenceArmor = 0;
            this.DefenceDodge = 0;
            this.DefenceParry = 0;
            this.DefenceBlock = 0;
            this.Resilience = 0;
            this.PvpPower = 0;

            this.Profession1 = "";
            this.Profession1Value = 0;
            this.Profession2 = "";
            this.Profession2Value = 0;
        }

        public override string ToString()
        {
            return this.ID;
        }
    }

    public class Characters
    {
        private Dictionary<string, CharacterData> _characterDB = new Dictionary<string, CharacterData>();
        public Dictionary<string, CharacterData> CharacterDB
        { get { return _characterDB; } }

        public string DefaultPath { get; set; }

        public Characters()
        {
            string path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData);
            path = System.IO.Path.Combine(path, @"Odelle\ArmoryData");

            this.DefaultPath = path;
        }

        ~Characters()
        {
        }

        public bool Save(string path)
        {
            if (_characterDB.Count == 0)
                return true;

            if (path == "")
            {
                path = this.DefaultPath;
            }
            path = System.IO.Path.Combine(path, @"Characters.armory.wow");

            XmlSerializer serializer = null;
            FileStream stream = null;

            try
            {
                serializer = new XmlSerializer(typeof(CharacterData[]));
            }
            catch (Exception)
            {
                return false;
            }

            try
            {
                stream = new FileStream(path, FileMode.Create);
            }
            catch (Exception)
            {
                return false;
            }

            try
            {
                CharacterData[] keyValues = _characterDB.Values.ToArray<CharacterData>();
                serializer.Serialize(stream, keyValues);
            }
            catch (Exception)
            {
            }
            finally
            {
                stream.Close();
            }

            return true;
        }

        public bool Load(string path)
        {
            if (path == "")
            {
                path = this.DefaultPath;
            }
            path = System.IO.Path.Combine(path, @"Characters.armory.wow");

            XmlSerializer serializer = null;
            FileStream stream = null;

            try
            {
                serializer = new XmlSerializer(typeof(CharacterData[]));
            }
            catch (Exception)
            {
                return false;
            }

            if (!System.IO.File.Exists(path))
                return false;

            stream = new FileStream(path, FileMode.Open);

            CharacterData[] keyValues = null; ;

            try
            {
                while (true)
                {
                    if (stream.CanRead)
                    {
                        keyValues = (CharacterData[])serializer.Deserialize(stream);
                        break;
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
            catch (Exception)
            {
            }

            if (keyValues == null)
            {
                _characterDB.Clear();
            }
            else
            {
                _characterDB = keyValues.ToDictionary<CharacterData, string>(c => c.ID);
            }
            stream.Close();

            return true;
        }

        public void Sort()
        {
            if (_characterDB.Count <= 1)
                return;

            CharacterData[] keyValues = _characterDB.Values.ToArray<CharacterData>();
            string[] keys = _characterDB.Keys.ToArray<string>();
            Array.Sort(keys, keyValues);

            _characterDB = keyValues.ToDictionary<CharacterData, string>(c => c.ID);
        }

        public CharacterData GetData(string Region, string Realm, string Character)
        {
            string ID = CharacterData.NamesToId(Region, Realm, Character);
            return GetData(ID);
        }

        public CharacterData GetData(string id)
        {
            if (id == "")
            {
                return null;
            }
            else if (_characterDB.ContainsKey(id))
            {
                return _characterDB[id];
            }
            else
            {
                return null;
            }
        }

        public bool SetData(CharacterData data)
        {
            if (data == null)
                return false;

            if (data.ID == "")
                return false;

            if (_characterDB.ContainsKey(data.ID))
            {
                _characterDB.Remove(data.ID);
            }

            _characterDB.Add(data.ID, data);

            return true;
        }

        public string[] GetRegionNames()
        {
            return AccessArmory.RegionNames;
        }

        public string[] GetRealmNames(string region)
        {
            if (this.CharacterDB.Count == 0)
                return new string[] { };

            List<string> names = new List<string>();

            for (int i = 0; i < this.CharacterDB.Count; ++i)
            {
                string key = this.CharacterDB.Keys.ElementAt<string>(i);

                string keyRegion = "";
                string keyRealm = "";
                string keyCharacter = "";
                Data.CharacterData.IdToNames(key, out keyRegion, out keyRealm, out keyCharacter);

                if (keyRegion == region)
                {
                    if (!names.Contains(keyRealm))
                    {
                        names.Add(keyRealm);
                    }
                }
            }
            names.Sort();

            return names.ToArray();
        }

        public string[] GetCharacterNames(string region, string realm)
        {
            if (this.CharacterDB.Count == 0)
                return new string[] { };

            return GetParametersList(region, realm, "Character");
        }

        public string[] GetGuildNames(string region, string realm)
        {
            if (this.CharacterDB.Count == 0)
                return new string[] { };

            return GetParametersList(region, realm, "Guild");
        }

        public string[] GetClassNames(string region, string realm)
        {
            if (this.CharacterDB.Count == 0)
                return new string[] { };

            return GetParametersList(region, realm, "ClassName");
        }

        public string[] GetParametersList(string region, string realm, string fieldName)
        {
            if (this.CharacterDB.Count == 0)
                return new string[] { };

            List<string> strValues = new List<string>();

            for (int i = 0; i < this.CharacterDB.Count; ++i)
            {
                string key = this.CharacterDB.Keys.ElementAt<string>(i);

                string keyRegion = "";
                string keyRealm = "";
                string keyCharacter = "";
                Data.CharacterData.IdToNames(key, out keyRegion, out keyRealm, out keyCharacter);

                if (keyRegion == region && keyRealm == realm )
                {
                    switch (fieldName.ToLower())
                    {
                        case "character":
                            if (!strValues.Contains(keyCharacter))
                                strValues.Add(keyCharacter);
                            break;
                        case "guild":
                            if (!strValues.Contains(this.CharacterDB[key].Guild))
                                strValues.Add(this.CharacterDB[key].Guild);
                            break;
                        case "classname":
                            if (!strValues.Contains(this.CharacterDB[key].ClassName))
                                strValues.Add(this.CharacterDB[key].ClassName);
                            break;
                        case "race":
                            if (!strValues.Contains(this.CharacterDB[key].Race))
                                strValues.Add(this.CharacterDB[key].Race);
                            break;
                        case "profession":
                            if (!strValues.Contains(this.CharacterDB[key].Profession1))
                                strValues.Add(this.CharacterDB[key].Profession1);
                            if (!strValues.Contains(this.CharacterDB[key].Profession2))
                                strValues.Add(this.CharacterDB[key].Profession2);
                            break;
                        case "spec":
                            if (!strValues.Contains(this.CharacterDB[key].Spec1))
                                strValues.Add(this.CharacterDB[key].Spec1);
                            if (!strValues.Contains(this.CharacterDB[key].Spec2))
                                strValues.Add(this.CharacterDB[key].Spec2);
                            break;
                        default:
                            break;
                    }
                }
            }
            if (strValues.Count > 0)
            {
                strValues.Sort();
            }

            return strValues.ToArray();
        }

    }
}
